#coding=utf-8
# Create your views here.
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render_to_response
from release.citylife.models import User, DoubanSubject, Job, Bid
from douban.service import DoubanService
from douban.client import OAuthClient
import urllib
from xmlnode import XMLNode
from django.core.exceptions import ObjectDoesNotExist
from django import forms
import StringIO
from PIL import Image
import hashlib
import os
from django.core.files import File

import logging
logging.basicConfig()
log = logging.getLogger(__name__)
hdlr = logging.StreamHandler()
formatter = logging.Formatter('%(filename)s, %(lineno)d')
hdlr.setFormatter(formatter)
log.addHandler(hdlr)
log.setLevel(logging.DEBUG)

class GV:
    MATCH_DIMENTION = 10
    PAGE_LENGTH = 10
    top_tags = [u'管理经济学', u'经济', u'金融', u'商业', u'投资', u'营销', u'广告', u'理财', u'股票', u'创业', u'企业史', u'策划', u'口译', u'实战口译', u'英语', u'翻译', u'英语学习', u'interpretation', u'学习', u'林超伦']

##### douban * #####
class Douban:
    sid = 'pseudoking'
    api_key = "0eea75a43af7c36f2cbf60ecb0e74a4d"
    secret = "49ad5b7e3a982962"
    request_tokens = {}
    access_tokens = {}

##### douban #####

##### facilities * #####

def login_validation(view_func):#decorator method
    def wrapper(*args, **kw):
        request = args[0]
        if not request.session.get('uid'):
            return HttpResponseRedirectAfter('/citylife/login/', 'have to login first')
        return view_func(*args, **kw)
    return wrapper

@login_validation
def authorization(view_func):
    def wrapper(*args, **kw):
        request = args[0]
        uid = request.session.get('uid')
        user = User.objects.get(id = uid)
        if(not (user.douban_access_key and user.douban_access_secret)):
            return HttpRequestRedirect('/citylife/register_import')
        return view_func(*args, **kw)
    return wrapper

def sendDoubanMessage(user, msg):
    """
    send a message [msg] to [user]
    """
    pass
    

def HttpResponseRedirectAfter(url, message, s=3000):
    """
    input:
        url|str
        message|str
        s|int
    desc:
        show [message] and redirect to [url] after [s] ms
    """
    return HttpResponse("""<body onload="javascript:setTimeout(function(){window.location.href='%s'},%d);">%s</body>""" % (url, s, message))

##### facilities #####

##### login * #####
class LoginForm(forms.Form):
    name = forms.CharField(max_length = 100, initial='pseudoking', required=False)
    psd = forms.CharField(max_length = 100, initial='11111111', required=False)
    
def login(request):
    if request.method == 'POST':  
        form = LoginForm(request.POST)        
        if form.is_valid(): 
            name = form.cleaned_data.get('name')
            psd = form.cleaned_data.get('psd')
            try:
                user = User.objects.get(name = name, psd = psd)
                request.session['uid'] = user.id
                return HttpResponseRedirect('/citylife/index/')
            except ObjectDoesNotExist:
                return HttpResponseRedirectAfter('/citylife/login/', 'wrong name or password')
    else:  
        form = LoginForm()
        return render_to_response('login.html', dict(form = form))

##### login #####

def logout(request):
    if(request.session.get('uid')):
        del request.session['uid']

    return HttpResponseRedirectAfter('/citylife/index/', 'logged out.')

##### account * #####
class AccountForm(forms.Form):
    photo_upload = forms.ImageField(required=False)
    short_intro = forms.CharField(max_length = 100, widget=forms.Textarea, initial='short introduction', required=False)
    long_intro = forms.CharField(max_length = 4096, widget=forms.Textarea, initial='long introduction', required=False)
    import_douban_id = forms.CharField(max_length = 128, required=False)
    import_douban_psd = forms.CharField(max_length = 128, required=False)

def __scale_dimensions(width, height, longest_side):
    if width < height:
        return (int(float(longest_side) / height * width), longest_side)
    else:
        return (longest_side, int(float(longest_side) /width * height))

def handle_uploaded_image(img):  
    """
    uploading work, but bug existing!
    """
    imagefile  = StringIO.StringIO(img.read())  
    imageImage = Image.open(imagefile)  
  
    (width, height) = imageImage.size  
    (width, height) = __scale_dimensions(width, height, longest_side=240)  
  
    resizedImage = imageImage.resize((width, height))  
  
    imagefile = StringIO.StringIO()
    if resizedImage.mode != "RGB":
        resizedImage = resizedImage.convert("RGB")    
    resizedImage.save(imagefile,'JPEG')  
    filename = hashlib.md5(imagefile.getvalue()).hexdigest()+'.jpg'
          
    path = 'media/%s' % filename
    imagefile = open(path, 'w')  
    resizedImage.save(imagefile,'JPEG')
    imagefile = open(path, 'r')  
    content = File(imagefile)  

    #my_object = MyDjangoObject()  
    #my_object.photo.save(filename, content)     

@login_validation
def account_setting(request):
    if request.method == 'POST':  
        form = AccountForm(request.POST, request.FILES)  
        
        print request.FILES

        if form.is_valid(): 
            log.info('form.is_valid')
            photo_upload = request.FILES.get('photo_upload')
            if photo_upload:
                log.info('photo_upload')
                handle_uploaded_image(photo_upload)

            short_intro = form.cleaned_data.get('short_intro')
            long_intro = form.cleaned_data.get('long_intro')
            
            user = User.objects.get(id = request.session['uid'])
            user.short_intro = short_intro
            user.long_intro = long_intro
            user.save()
            
    else:
        user = User.objects.get(id = request.session['uid'])
        data = dict(
                short_intro = user.short_intro,
                long_intro = user.long_intro,
                )
        form = AccountForm()
          
    return render_to_response('account_setting.html', dict(form = form))
##### account #####

##### job * #####
def job_index(request):
    jobs = Job.objects.all()
    return render_to_response('job_index.html', dict(jobs=jobs))

def create_job(request):
    return render_to_response('create_job.html')

def create_job_validate(request):
    title = request.POST.get('job_title')
    category = request.POST.get('job_category')
    desc = request.POST.get('job_desc')
    if(not (title and category and desc)):
        return HttpResponseRedirectAfter('/citylife/create_job/', 'lack of parameters', 3000)

    job = Job(title = title, category = category, desc = desc)
    job.save()
    jid = job.id
    
    return HttpResponseRedirect('/citylife/job_detail?jid=%d'%(jid))

def job_detail(request):
    jid = request.GET.get('jid')
    if(not jid):
        return HttpResponseRedirectAfter('/citylife/job_index/', 'lack of parameters', 3000)
    try:
        job = Job.objects.get(id=jid)
    except:
        return HttpResponseRedirectAfter('/citylife/job_index/', 'wrong parameters', 3000)
    
    biddings = []
    print 'len(job.bbids)', len(job.bbids)
    for bbid in job.bbids:
        if bbid:
            bid = Bid.objects.get(id = bbid)
            print '--------'
            print bid.uid
            print '--------'
            bidder = User.objects.get(id = bid.uid)
            biddings.append((bid, bidder))

    paras = dict(
            jid = jid,
            job_title = job.title, 
            job_category = job.category, 
            job_desc = job.desc,
            biddings = biddings,
            )
    return render_to_response('job_detail.html', paras)

def create_bid(request):
    jid = request.GET.get('jid')
    if(not jid):
        return HttpResponseRedirectAfter('/citylife/job_index/', 'lack of parameters', 3000)
    try:
        job = Job.objects.get(id=jid)
    except:
        return HttpResponseRedirectAfter('/citylife/job_index/', 'wrong parameters', 3000)    
    
    paras = dict(jid = jid)
    return render_to_response('create_bid.html', paras)

def create_bid_validate(request):
    uid = request.session.get('uid')
    jid = request.GET.get('jid')
    price = request.POST.get('bid_price')
    desc = request.POST.get('bid_desc')
    if(not (uid and jid and price and desc)):
        return HttpResponseRedirectAfter('/citylife/create_bid/', 'lack of parameters', 3000)

    try:
        job = Job.objects.get(id = jid)
    except:
        return HttpResponseRedirectAfter('/citylife/create_bid/', 'wrong parameters', 3000)

    bid = Bid(uid = uid, jid = jid, price = price, desc = desc)
    bid.save()

    job.bbids.append(str(bid.id))
    job.save()

    return HttpResponseRedirect('/citylife/job_detail/?jid=%s' % jid)
##### job #####
class DatingIndexForm(forms.Form):
    name = forms.CharField(max_length = 100, initial='pseudoking', required=False)

##### match * #####
def importSubject(user, url):
    """
    add the douban subjects in page [url] into db
    """
    log.info('importSubject(%s, %s)' % (user.name, url))

    import lxml.html as H
    import time
    
    url_opened = False
    for i in range(5):
        try:
            html = urllib.urlopen(url).read().decode('utf-8')
            url_opened = True
        except:
            time.sleep(3)

    if (not url_opened):
        log.info('urlopen failed, url=%s', url)
        return 0

    doc = H.document_fromstring(html)
    uls = doc.xpath('//div[contains(@class, "article")]/ul[@class="collect tlst"]')
    
    for ul in uls:
        url = ul.xpath('li/h3/a/@href')[0]
        title = ul.xpath('li/h3/a/text()')[0]
        
        subject, created = DoubanSubject.objects.get_or_create(url = url)
        if created:
            subject.title = title.encode('utf8')
            subject.url = url
            
            ##### get tags * #####
            url_opened = False
            for i in range(5):
                try:
                    html = urllib.urlopen(url).read().decode('utf-8')
                    url_opened = True
                except:
                    time.sleep(3)

            if (url_opened):
                doc = H.document_fromstring(html)
                tags = doc.xpath('//div[@class="related_info"]/div[@class="blank20"]/div/a/text()')
                
                subject.tags = tags
            else:
                log.info('urlopen failed, url=%s', url)
            
            ##### get tags #####
            subject.save()

        if subject.id not in user.douban_subject_ids:
            user.douban_subject_ids.append(subject.id)

    return len(uls)

def update():
    log.info('upload()')

    users = User.objects.all()

    ##### update the info of [user] from douban * #####
    for user in users:
        cat1s = ['book']
        cat2s = ['collect','wish','do']
        for cat1 in cat1s:
            for cat2 in cat2s:
                page_index = 0
                url = 'http://%s.douban.com/list/%s/%s' % (cat1, user.douban_id, cat2)
                while(1):
                    if importSubject(user, url) == 0:
                        break

                    url = 'http://%s.douban.com/list/%s/collect?start=%d&status=%s'%(cat1, user.douban_id, page_index, cat2)
                    page_index += 1
        user.save()
    ##### update the info of [user] from douban #####


    ##### calculate the coordination of each user * #####
    for user in users:     
        tmp_dic = {}
        for subject_id in user.douban_subject_ids:
            subject = DoubanSubject.objects.get(id = subject_id)
            
            for tag in subject.tags:
                if tag in tmp_dic.keys():
                    tmp_dic[tag] = tmp_dic[tag] + 1
                else:
                    tmp_dic[tag] = 1
        
        #printUnicodeDic(tmp_dic)

        coordination = []
        for top_tag in GV.top_tags:
            if top_tag in tmp_dic.keys():
                coordination.append(tmp_dic[top_tag])
            else:
                coordination.append(0)

        user.coordination = coordination
        user.save()
        
        print "user.coordination", user.coordination
    ##### calculate the coordination of each user * #####

def printUnicodeDic(dic):
    for (key, value) in dic.items():
        print key.encode('gbk'), value

def user_distance(user1, user2):
    c1, c2 = user1.coordination, user2.coordination
    return sum([abs(c1[i]-c2[i]) for i in range(len(c1))])

def match():
    """
    update the [matched_user_ids] of each user. First, compute the distance between each pair of users. Then, sorted the distances descending order. The [MATCH_THRESHOLD_RATIO*len(users)]-th distance is used as a threshold. Two users are matched if their distance is less than the threshold.
    """
    log.info('match()')

    users = User.objects.all()
    distance_mat = [[-1 for i in range(len(users))] for j in range(len(users))]
    distances = []
    for u1 in range(len(users)):
        for u2 in range(u1 + 1, len(users)):
            user1, user2 = users[u1], users[u2]
            distance_mat[u1][u2] = user_distance(user1, user2)
            distances.append(distance_mat[u1][u2])
    
    #match_threshold = sorted(distances, reverse = True)[int(GV.MATCH_THRESHOLD_RATIO * len(users))]
    match_threshold = 100
    for u1 in range(len(users)):
        for u2 in range(u1 + 1, len(users)):
            log.info(distance_mat[u1][u2])
            if distance_mat[u1][u2] < match_threshold:
                user1, user2 = users[u1], users[u2]
                user1.matched_user_ids.append(user2.id)
                user2.matched_user_ids.append(user1.id)
    
    #clear the redundant ids
    for u1 in range(len(users)):
        for u2 in range(u1 + 1, len(users)):
            user1, user2 = users[u1], users[u2]
            user1.matched_user_ids = list(set(user1.matched_user_ids))
            user2.matched_user_ids = list(set(user2.matched_user_ids))

    for user in users:
        user.save()
##### match #####

def dating_index(request):
    uid = request.session.get('uid')
    if uid:
        mates = dating_auto_match(uid)
    else:
        mates = User.objects.all()[0:min(GV.PAGE_LENGTH, User.objects.count())]

    paras = dict(
            mates = mates
            )
    return render_to_response('dating_index.html')

def index(request):
    return render_to_response('index.html')

class RegisterForm(forms.Form):
    email = forms.EmailField(initial = 'security.xling@gmail.com')
    psd = forms.CharField(max_length = 128, initial='11111111')
    name = forms.CharField(max_length = 128, initial='pseudoking')

def register(request):
    if request.method == 'POST':  
        form = RegisterForm(request.POST, request.FILES)  

        if form.is_valid(): 
            photo_upload = request.FILES.get('photo_upload')
            if photo_upload:
                handle_uploaded_image(photo_upload)

            email = form.cleaned_data['email']
            name = form.cleaned_data['name']
            psd = form.cleaned_data['psd']

            user = User(name = name, psd = psd, email = email)
            user.save()
            request.session['uid'] = user.id
            
            return HttpResponseRedirect('/citylife/register_import')
    else:
        form = RegisterForm()

    return render_to_response('register.html', dict(form = form))

@login_validation
def register_import_validate(request):
    uid = request.session['uid']
    douban_id = request.POST.get('douban_id')
    if(douban_id):
        request.session['douban_id'] = douban_id
        user = User.objects.get(id=uid)
        user.douban_id = douban_id
        user.save()
        
    client = OAuthClient(key= Douban.api_key, secret = Douban.secret)
    request_key = request.GET.get('oauth_token','')
    request_secret = Douban.request_tokens.get(request_key)

    log.info("request_key=%s" % request_key)
    log.info("request_secret=%s" % request_secret)

    if not (request_key and request_secret):
        key, secret1 = client.get_request_token() #获取未授权的 Request Token
        log.info('key=%s, secret1=%s'%(key, secret1))

        if key and secret1:
            Douban.request_tokens[key] = secret1
            url = 'http://' + request.META['HTTP_HOST'] + request.META['PATH_INFO'] + '?' + request.META['QUERY_STRING'] 
            url = client.get_authorization_url(key, secret1, callback=url)
            return HttpResponseRedirect(url)#请求用户授权 Request Token
        else:
            return HttpResponse('获取 Request Token 失败')
    else:
        try:
            access_key, access_secret, uid = client.get_access_token(request_key, request_secret) #获得用户授权 Request Token，用其换取Access Token
            if access_key and access_secret:# all check are passed, now we can do real things
                request.session['access_key'] = access_key
                request.session['access_secret'] = access_secret

                uid = request.session.get('uid')
                user = User.objects.get(id = uid)
                user.douban_access_key = access_key
                user.douban_access_secret = access_secret
                user.save()

                return HttpResponseRedirectAfter('/citylife/index', 'done')
            else:
                log.info('get_access_token 失败')
                return HttpResponse('get_access_token 失败')
        except Exception, e:
            print 'error:', e
            return HttpResponse('获取用户授权失败')        

def register_import(request):
    return render_to_response('register_import.html')

##### test * #####
def test(request):
    populate_db()
    update()
    match()
    
    return HttpResponse('......')

def populate_db():
    for user in User.objects.all():
        user.delete()    

    data_xml = file('media/citylife/users.xml').read()
    node = XMLNode.parseXML(data_xml)

    for user in node.user:
        _user = User(
                name = user.name[0].elementText, 
                email = user.email[0].elementText,        
                psd = user.psd[0].elementText,        
                douban_id = user.douban_id[0].elementText,
                #douban_subject_ids = [int(i) for i in user.douban_subject_ids[0].elementText.split('*')],
                )

        _user.save()
        

##### upload file * #####

class UploadFileForm(forms.Form):
    title = forms.CharField(max_length=50)
    file  = forms.FileField()

def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return HttpResponse('upload successed!')
    else:
        form = UploadFileForm()
    return render_to_response('test.html', {'form': form})

def handle_uploaded_file(f):
    destination = open('c:\\uploaded_file.dat', 'wb+')
    for chunk in f.chunks():
        destination.write(chunk)
    destination.close()
##### upload file #####
##### test * #####
